package me.bincker.formwork.web.core.filter

import com.fasterxml.jackson.databind.ObjectMapper
import me.bincker.formwork.web.core.exception.BadRequestException
import me.bincker.formwork.web.utils.ResponseUtils
import org.apache.commons.logging.LogFactory
import org.springframework.http.MediaType
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class UsernamePasswordLoginFilter: AbstractAuthenticationProcessingFilter(AntPathRequestMatcher("/login", "POST")) {
    companion object{
        val log = LogFactory.getLog(UsernamePasswordLoginFilter::class.java)
        val objectMapper = ObjectMapper()
    }

    init {
        this.setAuthenticationFailureHandler { _, _, authenticationException ->
            ResponseUtils.sendError(authenticationException.message ?: "账户异常")
        }

        this.setAuthenticationSuccessHandler { request, _, authentication ->
            request.session.setAttribute("user", authentication.details)
            ResponseUtils.send("登录成功")
        }

    }

    override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication {
        val mediaType : MediaType = MediaType.valueOf(request.contentType ?: throw BadRequestException())
        val usernamePassword =
                when {
                    MediaType.APPLICATION_JSON.includes(mediaType) ->
                        objectMapper.readValue(
                                InputStreamReader(request.inputStream, mediaType.charset ?: StandardCharsets.UTF_8),
                                UsernamePassword::class.java
                        )
                    MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType) ->
                        UsernamePassword(
                                request.getParameter("username")?.trim() ?: "",
                                request.getParameter("password") ?: ""
                        )
                    else -> throw BadRequestException()
                }

        val authRequest = UsernamePasswordAuthenticationToken(usernamePassword.username, usernamePassword.password)
        this.setDetails(request, authRequest)
        return this.authenticationManager.authenticate(authRequest)
    }

    private fun setDetails(request: HttpServletRequest, authRequest: UsernamePasswordAuthenticationToken) {
        authRequest.details = this.authenticationDetailsSource.buildDetails(request)
    }

    private data class UsernamePassword(
            var username: String = "",
            var password: String = ""
    )
}